﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace LookAHead
{
    class Grid
    {
        public int MaxX; //the max size of rows
        public int MaxY;//the max size of coulmns
        public double ObstaclePresentge; //the presentage of the grid
        public Cell[,] board; //will conatins all cess of grid
        public Cell goal;   //only saved  for index
        public Cell start;  //only saved  for index
        public int MaxG; //the inifinty value
        //all possible chilfren/operators in this gris that is 8 connected
        public static int[,] operators = { { -1, -1 }, { 1, -1 }, { -1, 1 }, { 1, 1 }, { 1, 0 }, { -1, 0 }, { 0, -1 }, { 0, 1 } };
        public MemBasedH[] GridMBH = new MemBasedH[10];
        /***********************************************************************************/
        public Grid(Grid Other)
        /* copy constructor for  Grid -  not in use*/
        {
            this.MaxG = Other.MaxG;
            this.MaxX = Other.MaxX;
            this.MaxY = Other.MaxY;
            this.ObstaclePresentge = Other.ObstaclePresentge;
            this.board = new Cell[MaxX, MaxY];
            for (int i = 0; i < MaxX; i++)
                for (int j = 0; j < MaxY; j++)
                    this.board[i, j] = new Cell(Other.Get(i, j));
            this.goal = new Cell(Other.goal);
            board[goal.x, goal.y] = this.goal;
            this.start = new Cell(Other.start);
            board[start.x, start.y] = this.start;
        }
        /***********************************************************************************/
        public Grid(int n, int m, double p)
        /* create Grid for save it to file*/
        {
            this.MaxX = n;
            this.MaxY = m;
            this.MaxG = MaxX * MaxY;
            board = new Cell[MaxX, MaxY];
            this.ObstaclePresentge = p;
            for (int i = 0; i < MaxX; i++)
                for (int j = 0; j < MaxY; j++)
                    if (board[i, j] == null)
                        board[i, j] = new Cell(i, j);
            CreateObstacles();
        }
        /***********************************************************************************/
        public Grid(int SizeX, int SizeY, int x_goal, int y_goal, int x_start, int y_start)
        /* creat Grid*/
        {
            this.MaxX = SizeX;
            this.MaxY = SizeY;
            this.MaxG = MaxX * MaxY;
            board = new Cell[MaxX, MaxY];
            this.ObstaclePresentge = 0;
            board[x_goal, y_goal] = this.goal = new Cell(x_goal, y_goal);
            this.goal.ChangeToGoal();
            board[x_start, y_start] = this.start = new Cell(x_start, y_start);
            this.start.ChangeToStart();
            this.start.CalcH(this.goal);
            for (int i = 0; i < MaxX; i++)
                for (int j = 0; j < MaxY; j++)
                    if (board[i, j] == null)
                    {
                        board[i, j] = new Cell(i, j);
                        //board[i, j].CalcH(goal);
                    }
        }
        /***********************************************************************************/
        public void ClearGrid()
        //function will clear data that was collect while running algoritem 
        {
            for (int i = 0; i < MaxX; i++)
                for (int j = 0; j < MaxY; j++)
                    board[i, j].Clear();
            board[goal.x, goal.y].ChangeToGoal();
            board[start.x, start.y].ChangeToStart();
        }
        /***********************************************************************************/
        public void ClearGrid(Stack<Cell> ClearStack)
        //function will clear data that was collect while running algoritem 
        {
            Cell c;
            double fu;
            Cell.status s;
            while (ClearStack.Count > 0)
            {
                c = ClearStack.Pop();
                fu = c.fu;
                s = c.hash_s;
                c.Clear();
                c.fu = fu;
                c.hash_s = s;
            }
        }
        /***********************************************************************************/

        public void CreateMBH()
        //randomized 10% cells that would calculet A*
        {
            int rand;
            Cell RemmberStart = start;
            Cell RemmberGoal = goal;
            start.ChangeToEmpty();
            goal.ChangeToEmpty();
            for (int i = 0; i < GridMBH.Length; i++)
            {
                do
                {
                    rand = program.random.Next(0, MaxG);
                    //this.start = new Cell(rand % MaxX, rand / MaxX);
                    this.start = this.Get(rand % MaxX, rand / MaxX);
                } while ((start.state != Cell.states.empty) && (!start.IsEqual(RemmberStart)) && (!start.IsEqual(RemmberGoal)));
                GridMBH[i] = new MemBasedH(this.start);
            }
            start = RemmberStart;
            start.ChangeToStart();
            goal = RemmberGoal;
            goal.ChangeToGoal();
            program.Init();
        }
        /***********************************************************************************/
        public void SetGoalStart()
        //for random board needed random goal and start
        {
            int rand;
            do
            {
                rand = program.random.Next(0, MaxX * MaxY);
                this.goal = new Cell(rand % MaxX, rand / MaxX);
            } while (goal.state == Cell.states.obstacle);
            goal.ChangeToGoal();
            board[rand % MaxX, rand / MaxX] = goal;
            do
            {
                rand = program.random.Next(0, MaxX * MaxY);
                this.start = new Cell(rand % MaxX, rand / MaxX);
                board[rand % MaxX, rand / MaxX] = start;
                start.ChangeToStart();
                start.CalcH(goal);
            } while ((goal.IsEqual(start)) || (start.state == Cell.states.obstacle));
        }
        /***********************************************************************************/
        public void CreateObstacles()
        //create the obstacle s in board in random
        {
            double obstacle_count = ObstaclePresentge / 100;
            obstacle_count = obstacle_count * MaxX * MaxY;
            int counter = 0;
            while (counter < obstacle_count)
            {
                int rand = program.random.Next(0, MaxX * MaxY);
                int y_ = rand / MaxX;
                int x_ = rand % MaxX;
                if (board[x_, y_].CreateObstacle())
                    counter++;
            }
        }
        /***********************************************************************************/
        //public Cell[] GetChildren(Cell Parent)
        ////return array of children of Cell c -- not in use in the moment
        //{
        //    Cell[] ans = new Cell[program.operators];
        //    int location = 0;
        //    for (int op = 0; op < program.operators; op++)
        //    {
        //        ans[location] = GetChild(Parent, op);
        //        if (ans[location] != null)
        //            location++;
        //    }
        //    return ans;
        //}
        /***********************************************************************************/
        public Cell GetChild(Cell Parent, int op)
        // return the child  number "op" to Cell c
        {
            int i = operators[op, 0], j = operators[op, 1];
            Cell child = null;
            if (Legal(Parent.x + i, Parent.y + j))
            {
                child = this.Get(Parent.x + i, Parent.y + j);
                if (!child.CouldGenrate(Parent))
                    child = null;
                else
                    child.CalcH(goal);
            }
            return child;
        }
        /***********************************************************************************/
        public bool Legal(int new_i, int new_j)
        //if cordinated new_i,new_j is a leagel Cell in Grid board
        {
            if ((new_i < 0) || (new_i >= this.MaxX))
                return false;
            if ((new_j < 0) || (new_j >= this.MaxY))
                return false;
            if (program.GridBoard.Get(new_i, new_j).state == Cell.states.obstacle)
                return false; //obstacle is blocking us to move
            return true;
        }
        /***********************************************************************************/
        public Cell Get(int i, int j)
        //return Cell in coordinate    i,j
        {
            return board[i, j];
        }
        /***********************************************************************************/
        public void Set(int i, int j, Cell c)
        //function will Set value c in Cell coordinate   i,j
        {
            board[i, j] = c;
        }
        /***********************************************************************************/
        public void PrintGrid()
        //priting Grid to screen
        {
            for (int i = 0; i < MaxX; i++)
            {
                for (int j = 0; j < MaxY; j++)
                    Console.Write((int)board[i, j].state + ",");
                Console.WriteLine();
            }
            Console.WriteLine();
        }
        /***********************************************************************************/
        public void PrintGridToFile(String file)
        // printing the Grid to file "file"
        {
            FileStream stream = new FileStream(file, FileMode.Create);
            StreamWriter writer = new StreamWriter(stream);
            StringBuilder ans = new StringBuilder();
            writer.WriteLine("type octile");
            writer.WriteLine("height " + MaxX);
            writer.WriteLine("width " + MaxY);
            writer.WriteLine("x_goal " + goal.x);
            writer.WriteLine("y_goal " + goal.y);
            writer.WriteLine("x_start " + start.x);
            writer.WriteLine("y_start " + start.y);
            writer.WriteLine("map");
            for (int i = 0; i < MaxX; i++)
            {
                ans = new StringBuilder();
                for (int j = 0; j < MaxY; j++)
                {
                    if (((int)board[i, j].state) == 0)
                        ans.Append(".");
                    else if (((int)board[i, j].state) == 1)
                        ans.Append("@");
                    else if (((int)board[i, j].state) == 5)
                        ans.Append("s");
                    else if (((int)board[i, j].state) == 4)
                        ans.Append("g");
                }
                writer.WriteLine(ans);
                writer.Flush();
            }
            writer.Close();
            stream.Close();
        }
        /***********************************************************************************/
        public Solution GetPath(Cell child)
        //return an object with depth,cost,anspath of Cell ans c
        {
            String ans = "";
            Cell parent = child;
            int depth = -1;
            double cost = 0;
            while (parent != null)
            {
                ans = parent.x + "," + parent.y + " --> " + ans;
                depth++;
                parent = parent.parent;
            }
            cost = child.g;
            Solution s = new Solution(depth, cost, child, ans);
            return s;
        }
        /***********************************************************************************/
        public void printPathToSol()
        //prints the path from start to goal 
        {
            if (program.ans == null)
            {
                Console.WriteLine("No Solution!!!");
                return;
            }
            if (((program.CurrentAlgo == program.LookAHeadNum) || (program.CurrentAlgo == program.LookAHeadWithBpmxNum)) && (!program.SolFoundInAstarLk))
            {
                Console.WriteLine("Print Solution");
                Console.WriteLine("the number of moves is " + (program.SolDepth));
                Console.WriteLine("the cost is " + program.SolCost);
                return;
            }
            else
            {
                Solution s;
                s = GetPath(program.ans);
                Console.WriteLine("Print Solution");
                Console.WriteLine(s.ansPath);
                Console.WriteLine("the number of moves is " + (s.depth));
                Console.WriteLine("the cost is " + s.cost);
                program.SolCost = s.cost;
                program.SolDepth = s.depth;
            }
        }
        /***********************************************************************************/
    }
}
